Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

P6295 有标号 DAG 计数

题意

\(n\) 个点有标号弱联通 DAG 数量。

推导

\(f_i\) 表示 \(i\) 个点有标号 DAG 数量(不保证弱联通),有: \[ f(i)=\sum_{j=1}^i\binom ij(-1)^{j-1}f(i-j)2^{j(i-j)} \] 意义为选至少 \(j\) 个度数为零的点,向剩下的 \(i-j\) 个点随便连有向边,容斥一下就得到了上式。

下面进行推导。根据一个 trick: \[ j(i-j)=\binom i2-\binom j2-\binom {i-j}2 \] 所以有: \[ \begin{aligned} &f(i)=\sum_{j=1}^i\frac{i!}{j!(i-j)!} (-1)^{j-1} f(i-j) \frac{2^\binom i2}{2^\binom j22^\binom{i-j}2}\\ \Rightarrow&\frac{f(i)}{i!2^\binom i2}=\sum_{j=1}^i\frac{(-1)^{j-1}}{j!2^\binom j2} \frac{f(i-j)}{(i-j)!2^\binom{i-j}2} \end{aligned} \]\[ \begin{aligned} F(x)=\sum_{i=0}^\infty\frac{f(i)}{i!2^{\binom i2}}\\ G(x)=\sum_{i=1}^\infty\frac{(-1)^{i-1}}{i!2^\binom i2} \end{aligned} \] 则有 \[ F(x)=F(x)G(x)+1 \] 加 1 是因为常数项为 1.

解得 \[ F(x)=\frac1{1-G(x)} \] 根据 \(F\) 的定义,我们解出 \(F\) 后乘上 \(2^\binom i2\) 即为 \(f\) 的 EGF。根据多项式 ln 和 exp 的组合意义,我们将得到的 EGF ln 一下即可得到题目要求的弱联通的 DAG 数量的 EGF。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=x*10+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=4e5+10,mod=998244353,g=3,gi=998244354/3;
inline int fpow(int a,long long b){int ans=1;for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod; return ans;}
struct NTT{
int r[maxn],lim;
inline void getr(int li){lim=li;for(int i=0;i<=lim;i++) r[i]=(r[i>>1]>>1)|((i&1)*(lim>>1));}
void operator () (int *a,int type) const {
for(int i=0;i<lim;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int mid=1;mid<lim;mid<<=1){
int rt=fpow(type==1?g:gi,(mod-1)/(mid<<1));
for(int r=mid<<1,j=0;j<lim;j+=r){
int p=1;
for(int k=0;k<mid;k++,p=1ll*p*rt%mod){
int x=a[j+k],y=1ll*a[j+k+mid]*p%mod;
a[j+k]=(x+y)%mod,a[j+k+mid]=(x-y+mod)%mod;
}
}
}
if(type==-1) for(int p=fpow(lim,mod-2),i=0;i<lim;i++) a[i]=1ll*a[i]*p%mod;
}
}ntt;
void Inv(const int *a,int *ans,int n){
if(n==1) return ans[0]=fpow(a[0],mod-2),ans[1]=0,void();
static int res[maxn];
Inv(a,ans,n>>1);
int lim=n<<1;
ntt.getr(lim);
for(int i=0;i<n;i++) res[i]=a[i];
for(int i=n;i<lim;i++) ans[i]=res[i]=0;
ntt(ans,1),ntt(res,1);
for(int i=0;i<lim;i++) ans[i]=ans[i]*(2-1ll*ans[i]*res[i]%mod+mod)%mod;
ntt(ans,-1);
for(int i=n;i<lim;i++) ans[i]=0;
}
inline void deri(const int *a,int *ans,int n){for(int i=1;i<n;i++) ans[i-1]=1ll*a[i]*i%mod;ans[n-1]=0;}
inline void inte(const int *a,int *ans,int n){for(int i=n-1;i;i--) ans[i]=1ll*a[i-1]*fpow(i,mod-2)%mod;ans[0]=0;}
inline void ln(const int *a,int *ans,int n){
static int res[maxn];
Inv(a,ans,n);
deri(a,res,n);
int lim=n<<1;
ntt.getr(lim);
ntt(ans,1),ntt(res,1);
for(int i=0;i<lim;i++) res[i]=1ll*ans[i]*res[i]%mod,ans[i]=0;
ntt(res,-1);
for(int i=n;i<lim;i++) res[i]=0;
inte(res,ans,n);
}
int a[maxn],b[maxn],n,inv[maxn],mul[maxn];
inline void work(){
n=read();
inv[0]=mul[0]=1;for(int i=1;i<=n;i++) mul[i]=1ll*mul[i-1]*i%mod;
inv[n]=fpow(mul[n],mod-2);for(int i=n-1;i;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
for(int i=1;i<=n;i++) a[i]=(i&1?mod-1ll:1ll)*inv[i]%mod*fpow(fpow(2,1ll*i*(i-1)/2),mod-2)%mod;
a[0]=1;
int lim=1;for(;lim<=n;lim<<=1);
Inv(a,b,lim);
for(int i=0;i<=n;i++) b[i]=1ll*b[i]*fpow(2,1ll*i*(i-1)/2)%mod,a[i]=0;
for(int i=n+1;i<lim;i++) a[i]=b[i]=0;
ln(b,a,lim);
for(int i=1;i<=n;i++) printf("%lld\n",1ll*a[i]*mul[i]%mod);
}
}
signed main(){
star::work();
return 0;
}

给小狼留言